﻿using net_ddns_server_ali.model;
using Symbol.Collections.Generic;

namespace net_ddns_server_ali
{
    /// <summary>
    /// 域名IP同步
    /// </summary>
    public class DomainIPSyncService
    {

        #region 字段
        private readonly Config _config;
        private ILog _log;
        private System.Timers.Timer _timer;
        private NameValueCollection<AliyunDNS> _list_dns;
        #endregion

        #region 属性

        public ILog Log
        {
            get { return _log; }
            set { _log = value; }
        }

        #endregion

        #region 构造函数

        public DomainIPSyncService(FastObject config)
            : this(JSON.ToObject<Config>(config.ToJson()))
        {
        }

        public DomainIPSyncService(Config config)
        {
            _list_dns = new NameValueCollection<AliyunDNS>();
            _config = config;
            foreach (var item in _config.Domains)
            {
                item.Value.Domain = item.Key;
            }
        }
        #endregion

        #region methods

        #region Start
        public void Start()
        {
            int interval = _config.Interval;
            if (interval < 1)
                interval = 10;
            _timer = new System.Timers.Timer
            {
                Interval = interval * 1000,
                AutoReset = true
            };
            _timer.Elapsed += Timer_Elapsed;
            _timer.Start();
            if (interval > 20)
            {
                ThreadHelper.Delay(1000, DoWork);
            }
        }

        void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            DoWork();
        }
        #endregion

        #region Stop
        public void Stop()
        {
            if (_timer == null)
                return;
            _timer.Elapsed -= Timer_Elapsed;
            _timer?.Stop();
            _timer?.Dispose();
            _timer = null;
        }
        #endregion

        #region DoWork
        void DoWork()
        {
            _timer?.Stop();
            try
            {
                DoWorkBody();
            }
            catch (Exception error)
            {
                Log?.Error(error);
            }
            finally
            {
                GC.Collect();
                _timer?.Start();
            }
        }
        void DoWorkBody()
        {
            Log?.Info("开始执行...");
            foreach (var item in _config.Domains.Values)
            {
                DoWork_Domain(item);
            }
            Log?.Info("执行结束");
        }
        void DoWork_Domain(DomainInfo domainInfo)
        {
            Log?.Info($"\t>domain: {domainInfo.Domain} ->{domainInfo.Provider}");
            var dns = CreateAliyunDNS(domainInfo.AccessKey);
            if (dns == null)
            {
                Log?.Warning($"\t创建DNS失败，AccessKeys错误。accessKey={domainInfo.AccessKey}");
                return;
            }
            foreach (var item in domainInfo.Items)
            {
                string _publicIP;
                if (item.Type == "AAAA")
                {
                    _publicIP = GetPublicIPv6();
                }
                else
                {
                    _publicIP = GetPublicIPv4();
                }

                Log?.Info($"\t\t>{item.Name}.{domainInfo.Domain} {item.Type} {_publicIP} ... ");
                if (dns.ModifyA(domainInfo.Domain, item.Type, item.Name, _publicIP))
                {
                    Log?.Info("\t修改成功");
                }
                else
                {
                    if (dns.AddA(domainInfo.Domain, item.Type, item.Name, _publicIP, item.TTL))
                    {
                        Log?.Info("\t创建成功");
                    }
                    else
                    {
                        Log?.Error("\t创建失败");
                    }
                }
            }
        }
        AliyunDNS CreateAliyunDNS(string accessKey)
        {
            var result = _list_dns[accessKey];
            if (result == null)
            {
                if (!_config.AccessKeys.TryGetValue(accessKey, out AccessKeyInfo accessKeyInfo))
                {
                    return null;
                }
                result = new AliyunDNS(accessKeyInfo.AccessKeyId, accessKeyInfo.AccessKeySecret);
                _list_dns[accessKey] = result;
            }
            result.Log = Log;
            return result;
        }

        #endregion

        #region GetPublicIP

        string GetPublicIPv4()
        {
            string url = _config.ShowIPv4Url;
            if (string.IsNullOrEmpty(url))
            {
                Log?.Warning("读取配置错误：缺少ShowIPv4Url");
                return null;
            }
            Log?.Info($"\t获取公网IPv4 '{url}'");
            return GetPublicIP(url);
        }

        string GetPublicIPv6()
        {
            string url = _config.showIPv6Url;
            if (string.IsNullOrEmpty(url))
            {
                Log?.Warning("读取配置错误：缺少ShowIPv6Url");
                return null;
            }
            Log?.Info($"\t获取公网IPv6 '{url}'");
            return GetPublicIP(url);
        }

        string GetPublicIP(string url)
        {
            string html = null;
            try
            {
                using Symbol.Net.Downloader downloader = new();
                downloader.RetryCount = 0;
                downloader.Encoding = System.Text.Encoding.UTF8;
                html = downloader.DownloadString(url);
                Log?.Info($"\tIP地址响应报文：{html}");
                if (html != null && html.StartsWith("{"))
                {
                    html = JSON.Parse(html).Path("ip").Convert<string>();
                }
            }
            catch (Exception error)
            {
                Log?.Error(error);
            }
            try
            {
                if (System.Net.IPAddress.Parse(html) != null)
                    return html;
            }
            catch (Exception error)
            {
                Log?.Error(error);
            }
            return null;
        }
        #endregion


        #endregion

    }
}
